%matplotlib inline
import ogr
import shapely
from shapely.geometry import *
import geopandas as gpd
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import IPython.display as display
Encargados de planeación en las ciudades esperan medir el impacto que tienen las medidas o proyectos de infraestructura que se ejecutan sobre las ciudades. Pocas veces se cuentan con las herramientas suficientes para medir este impacto. En ese sentido, Uber MOVEMENT le brinda herramientas a las personas involucradas en la toma de decisiones de una ciudad. Uber MOVEMENT provee los tiempos agregados de los viajes realizados entre las diferentes zonas de la ciudad de Bogotá D.C.
En el taller de Pandas, se había formulado la pregunta de cuanto había mejorado la movilidad (luego de la implementación del deprimido de la 94) entre la zona de la AV Cra. 30 entre 26 y Américas (Supercade de la 30), y la zona cercana a Unicentro. Se evidenció que entre el segundo trimestre de 2016 y el segundo trimestre de 2017, hubo una mejora de hasta el 28% de los tiempos de viaje entre el Supercade de la 30 y Unicentro.
En esta ocasión queremos visualizar la mejora de la movilidad no solo hacia Unicentro, sino hacia todos los sectores que probablemente hagan uso del deprimido de la 94 como vía de acceso. Sin embargo, la información agregada de Uber brinda una oportunidad para medir la accesibilidad a puntos de interés como hospitales. Dicha información permite medir qué tan accesible es un hospital en determinada área.
Por otro lado, el objetivo de los tomadores de decisiones de la ciudad es reducir la brecha de desigualdad, generando alternativas serias de movilidad para las personas más vulnerables, brindando las mismas oportunidades a personas con condiciones socioeconómicas tanto favorables como vulnerables. La estratificación clasifica grupos de personas que tienen condiciones sociales y económicas similares Mayor información sobre estratificación. A pesar de que la estratificación se hace netamente con la úbicación del predio, un análisis válido consiste en determinar la accesibilidad de las zonas de estratos bajos con respecto a los estratos altos. El ejercicio final propuesto en este taller, consiste en analizar la composición de estratos por cada localidad y su accesibilidad a través de la comparación de tiempos de viaje hacía los diferentes estratos
Durante este taller se reforzarán las siguientes áreas:
Para la realización de este taller se requiere la instalación de gdal.
conda install gdal
RECOMENDACIONES:
Ejercicio. En el taller de Pandas observamos la mejora entre dos zonas que probablemente hacen uso del deprimido de la 94 como vía de acceso. En este punto el objetivo es construir un mapa cloroplético que permita conocer en qué porcentaje mejoró o emperó la movilidad entre el segundo bimestre de 2016 y el segundo bimestre de 2017. Recordemos que el deprimido fue inaugurado en Marzo de 2017.
Para este punto se van a usar los siguientes archivos:
bogota_cadastral.json: contiene la información georeferenciada de 1160 zonas que componen a la ciudad de Bogotá D.C. (Incluído Sumapaz)bogota-cadastral-2017-2-OnlyWeekdays-HourlyAggregate.csv: contiene la información de viajes entre semana entre un origen y un destino en la ciudad de Bogotá, para el segundo trimestre de 2017. Se encuentra discriminada por la hora en la que se realizó el viaje.bogota-cadastral-2016-2-OnlyWeekdays-HourlyAggregate.csv: contiene la información de viajes entre semana entre un origen y un destino en la ciudad de Bogotá, para el segundo trimestre de 2016. Se encuentra discriminada por la hora en la que se realizó el viaje.Cargue la información catastral de Bogotá contenida en el archivo data/uber/bogota_cadastral.json. Esta información puede ser cargada con GeoPandas. Por otro lado, cargue la información de viajes entre semana agregados hora a hora para el segundo trimestre de 2016 y para el segundo trimestre de 2017.
import geopandas as gpd
df_bogota = gpd.read_file('/home/dianar/ML/TallerI/bogota_cadastral.json')
df_bogota.MOVEMENT_ID = df_bogota.MOVEMENT_ID.astype(np.int64)
df_2017_2 = pd.read_csv('/home/dianar/ML/TallerI/bogota-cadastral-2017-2-OnlyWeekdays-HourlyAggregate.csv')
df_2016_2 = pd.read_csv('/home/dianar/ML/TallerI/bogota-cadastral-2016-2-OnlyWeekdays-HourlyAggregate.csv')
Usando .loc filtre aquellos registros de df_bogota que no reporten ningún viaje en df_2017_2. Guarde los datos filtrados en df_bogota
# ESCRIBA SU CÓDIGO AQUÍ
sources=df_2017_2["sourceid"]
df_bogota=df_bogota.loc[df_bogota["MOVEMENT_ID"].isin(sources.unique())]
df_bogota.head()
# PRUEBE SU CÓDIGO AQUÍ
df_bogota.head()
df_2017_2.head()
Como observa en la parte superior. df_2017_2 contiene por cada fila un origen y un destino a determinada hora del día. Para cada uno de estos registros muestra la información del tiempo de viaje promedio, la desviación estándar, la media geométrica del tiempo y la desviación estándar de la media geométrica.
IMPORTANTE:
Cada origen y destino registrado acá corresponde a una zona registrada en el archivo bogota_cadastral.json. La columna que une ambos atributos se llama MOVEMENT_ID
Si deseamos filtrar todos los viajes a las 7pm, lo podemos hacer usando:
df_2017_2.loc[df_2017_2['hod'] == 19].head()
Defina una función que regrese un dataframe y permita filtrar df_2017_2 o df_2016_2 por un origen definido y una hora definida. En la siguiente celda puede encontrar un ejemplo de como debe ser llamada esta función y qué resultado debe generar. El valor 644 corresponde precisamente a la zona dónde se encuentra el supercade de la 30.
# Construya esta función
def filter_df_by_source_hod(df, sourceid, hod, value='mean_travel_time'):
# FIltre por hora y sourceid
tmp_df = df[(df['sourceid'] == sourceid ) & (df['hod'] == hod)]
# Regrese únicamente los destinos acompañados por su tiempo promedio de viaje
tmp_df = tmp_df[['dstid','mean_travel_time']]
return tmp_df
df = filter_df_by_source_hod(df_2016_2, 644, 19, value='mean_travel_time')
# Verifique ue la función haya sido definida de forma correcta con este código de prueba
#
df = filter_df_by_source_hod(df_2016_2, 644, 19, value='mean_travel_time')
df.head()
Como se puede evidenciar, usted acaba de extraer los tiempos promedios a cada zona en la ciudad de Bogotá. Ahora debe hacer una unión de atributos entre el dataframe df y df_bogota. Esto con el objetivo de conocer los nombres y los datos geográficos de cada uno de los destinos que están consignados en df.
df.rename(columns={"dstid": "MOVEMENT_ID", 'mean_travel_time': "time_2016"}, inplace=True)
# PONGA SU CÓDIGO AQUÍ
# Haga un spatial merge entre df_bogota y df, usando la columna MOVEMENT_ID
df_times = df_bogota.merge(df, on='MOVEMENT_ID')
# Cree una nueva columna que represente el tiempo en minutos.
df_times['time_minutes_2016'] = df_times['time_2016'] / 60
# PRUEBE SU CÓDIGO AQUÍ
df_times.head()
Para propósitos de visualización, vamos a recoger los centroides de las zonas donde se encuentran el deprimido de la 94 (953) y el supercade de la 30 (644)
source_dest_points = df_bogota.loc[df_bogota['MOVEMENT_ID'].isin([644, 953])]
cents = source_dest_points.centroid
cents.head()
Use df_times.plot() para realizar un mapa cloroplético.
f, ax = plt.subplots(1, figsize=(15, 10))
ax.set_title(u'Tiempo promedio de viaje desde el Supercade de la 30 en el 2016')
##### INGRESE AQUÍ SU CÓDIGO
df_times.plot(column='time_minutes_2016', scheme = 'Fisher_Jenks', k=10, cmap = 'Blues', ax = ax, legend = True, figsize=(15, 10))
# Imprima un mapa cloroplético usando la columna time_minutes_2016 en
# el GeoDataFrame df_times. El mapa debe tener un título, un colormap definido,
# debe estar definido en 10 categorías.
##### NO MODIFIQUE EL CÓDIGO ABAJO
df_bogota.plot(color='None', edgecolor='black', linewidth=0.1, ax=ax)
cents.plot(color='Red', ax=ax)
props = dict(boxstyle='round', facecolor='linen', alpha=1)
for point, txt in zip(cents.iteritems(), ['SUPERCADE', '94']):
ax.text(point[1].x-0.015,
point[1].y+0.01,
txt,
horizontalalignment='center',
fontsize=9,
bbox=props)
ax.set_axis_off()
plt.axis('equal')
# NO MODIFIQUE ESTA CELDA
Aplique el mismo flujo de trabajo con df_2016_2, pero esta vez para df_2017_2. Siga las instrucciones a continuación.
value = 'mean_travel_time'
df = filter_df_by_source_hod(df_2017_2, 644, 19, value)
df.rename(columns={"dstid": "MOVEMENT_ID", value: "time_2017"}, inplace=True)
# INGRESE SU CÓDIGO AQUÍ
# Haga una unión de atributos entre df_times y el df que acaba de definir arriba
# Cree la columna time_minutes_2017 que represente el tiempo en minutos
df_times = gpd.GeoDataFrame.merge(df_times, df, on='MOVEMENT_ID')
df_times['time_minutes_2017'] = df_times['time_2017'] / 60
Ahora, debemos construir una columna que exprese en terminos porcentuales, la mejora de tiempos. Recuerde que si el tiempo a un destino es el mismo entre 2016 y 2017, el valor que debe tomar esta columna es cero.
# INGRESE SU CÓDIGO AQUÍ
# Cree una columna diff que exprese el cambio porcentual en tiempos desde 2016_2 hacía 2017_2
df_times["diff"]=(df_times["time_minutes_2016"]-df_times["time_minutes_2017"])*100/df_times["time_minutes_2017"]
df_times.head()
# PRUEBE SU CÓDIGO AQUÍ
df_times.head()
Genere el mapa cloroplético usando la columna diff como columna de clasificación. Use el Colormap plt.cm.coolwarm. Puede usar el código del anterior mapa como base.
f, ax = plt.subplots(1, figsize=(15, 10))
ax.set_title(u'Tiempo promedio de viaje desde el Supercade de la 30 en el 2016')
##### INGRESE AQUÍ SU CÓDIGO
# Imprima un mapa cloroplético usando la columna time_minutes_2016 en
# el GeoDataFrame df_times. El mapa debe tener un título, un colormap definido,
# debe estar definido en 10 categorías.
df_times.plot(column='diff', cmap= plt.cm.coolwarm, ax = ax, legend = True, figsize=(15,10))
##### NO MODIFIQUE EL CÓDIGO ABAJO
df_bogota.plot(color='None', edgecolor='black', linewidth=0.1, ax=ax)
cents.plot(color='Red', ax=ax)
props = dict(boxstyle='round', facecolor='linen', alpha=1)
for point, txt in zip(cents.iteritems(), ['SUPERCADE', '94']):
ax.text(point[1].x-0.015,
point[1].y+0.01,
txt,
horizontalalignment='center',
fontsize=9,
bbox=props)
ax.set_axis_off()
plt.axis('equal')
# NO MODIFIQUE ESTA CELDA
Tip: Consulte más acerca de los Colormaps de Matplotlib, en caso de que desee usar otro mapa de colores.
osm = """
<iframe width="500" height="400" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" \
src="http://www.openstreetmap.org/export/embed.html?bbox=\
-74.0918%2C4.6214%2C-74.0685%2C4.6484&layer=mapnik" \
style="border: 1px solid black"></iframe><br/><small><a \
href="http://www.openstreetmap.org/#map=17/37.78292/-122.46336">View Larger Map</a></small>
"""
display.HTML(osm)
OpenStreetMap se alimenta de la información generada por los usuarios para georeferenciar lugares como avenidas, parques, hospitales, cafe, entre otros. OpenStreetMap nos permite descargar un OSM file (Ejemplo), que contiene la información acerca de las vías, los puntos de interés, los caminos y todos los datos georeferenciados de una zona seleccionada. Para este caso se han descargado los puntos de interés de la zona de Teusaquillo. A partir de esta zona, se descargaron todos los datos disponibles por OSM.
driver=ogr.GetDriverByName('OSM')
data = driver.Open('/home/dianar/ML/TallerI/teusaquillo.osm')
layer = data.GetLayer('points')
features=[x for x in layer]
print(len(features))
Existen tan solo en esta zona 4926 puntos georeferenciados. Los puntos de interés se encuentran como amenity, y deben ser extraídos como puntos. La clase Point descrita en el tutorial de GeoPandas, define todo lo necesario para manejarlos como figuras geométricas.
data_list=[]
for feature in features:
data=feature.ExportToJson(as_object=True) # Se convierte a JSON
coords=data['geometry']['coordinates'] # Extraemos las coordenadas
shapely_geo=Point(coords[0],coords[1]) # Usamos shapely para representar las coordenadas
name=data['properties']['name'] # Extraemos el nombre
highway=data['properties']['highway'] # Extraemos la dirección
other_tags=data['properties']['other_tags']
if other_tags and 'amenity' in other_tags:
feat=[x for x in other_tags.split(',') if 'amenity' in x][0] # Se filtran aquellos tags que correspondan a los amenities
amenity=feat[feat.rfind('>')+2:feat.rfind('"')] # Se extraen los amenities
else:
amenity=None
data_list.append([name, highway, amenity, shapely_geo]) # Data_list contiene en cada fila, un punto de interés
# A continuación se crea un GeoDataFrame con las observaciones y el sistema de coordenadas (CRS)
gdf=gpd.GeoDataFrame(data_list, columns=['Name','Highway','Amenity','geometry'], crs={'init': 'epsg:4326'})
gdf.tail()
Filtre aquellos puntos de interés que corresponda a hospital
# INGRESE SU CÓDIGO AQUÍ
hospital_df=gdf[gdf["Amenity"]=="hospital"]
hospital_df
# NO MODIFIQUE ESTA CELDA, USELA PARA PROBAR LA CREACIÓN DEL DATAFRAME hospital_df
hospital_df = hospital_df[hospital_df['Name'].notnull()] # Filtra aquellos hospitales con nombre nulo
G = hospital_df["geometry"].apply(lambda geom: geom.wkb) # Filtra aquellos hospitales duplicados por ubicación
hospital_df = hospital_df.loc[G.drop_duplicates().index]
hospital_df
A continuación encuentra un mapa que refleje la ubicación de los hospitales obtenidos
#df_bogota_filtered = df_bogota[df_bogota['scanombre'] != 'SUMAPAZ']
fig, ax = plt.subplots(figsize=(10,10))
ax.set_title(u'Hospitales alrededor de la UNAL')
df_bogota.plot(color='None', edgecolor='black', linewidth=0.1, ax=ax)
ax.set_axis_off()
plt.axis('equal')
for i,row in hospital_df.iterrows():
x=row['geometry'].x
y=row['geometry'].y
plt.annotate(row['Name'], xy=(x, y), size=7, xytext=(0, 5), textcoords='offset points')
plt.plot(x, y,'o', color='#f16824')
ax.set(aspect=1)
Cada punto que presenta un hospital, debe ser relacionado con el dataframe df_bogota. Recordemos que df_bogota guarda la información correspondiente a las zonas de UBER Movement. Por consiguiente es importante referenciar en qué zona se encuentran los puntos que hayamos usando OpenStreetMap
Use una unión espacial para asociar ambos dataframes (df_bogota y df_hospital) y que genere el resultado mostrado dos celdas abajo.
# INGRESE SU CÓDIGO AQUÍ
hospital_df = gpd.sjoin(hospital_df, df_bogota, how="inner", op='intersects')
# PRUEBE SU CÓDIGO AQUÍ
hospital_df
# NO MODIFIQUE ESTA CELDA
hospital_df
MOVEMENT_ID corresponde a la zona de Uber, donde se encuentra el hospital.
Ahora vamos a filtrar el conjunto de datos de Uber, por las zonas de destino que contienen a los hospitales. Para esto primero filtramos df_2017_2 por las celdas cuya columna dstid se encuentre en las zonas de los hospitales. Posteriormente, agrupamos los tiempos de viaje desde los diferentes orígenes y escogemos como función de agregación la función min().
df_2017_2.head()
# INGRESE SU CÓDIGO ACÁ
sources=hospital_df["MOVEMENT_ID"]
#df_bogota=df_bogota.loc[df_bogota["MOVEMENT_ID"].isin(sources.unique())]
df_2017_hospital_times = df_2017_2[df_2017_2["dstid"].isin(sources.unique())]
df_2017_hospital_times = df_2017_hospital_times.groupby("sourceid").min()
# PRUEBE SU CÓDIGO ACÁ
df_2017_hospital_times.head()
#NO MODIFIQUE ESTA CELDA
df_2017_hospital_times.head()
Aplicamos las siguientes operaciones para obtener los tiempos mínimos desde cualquier zona de Bogotá hacía las zonas de los hospitales demarcados.
# En orden hacemos lo siguiente:
# Linea 1 - Convertimos el indice en una columna más
# Linea 2 - Extraemos solo la columna de origen y de tiempo promedio
# Línea 3 - Renombramos las columnas
# Línea 4 - Imprimimos los primeros 5 registros
df_2017_hospital_times.reset_index(inplace=True)
df_2017_hospital_times = df_2017_hospital_times[['sourceid', 'mean_travel_time']]
df_2017_hospital_times.rename(columns={"sourceid": "MOVEMENT_ID", "mean_travel_time": "time"}, inplace=True)
df_2017_hospital_times.head()
Fusione df_bogota con df_2017_hospital_times en la columna MOVEMENT_ID. Cree una nueva columna que corresponde al tiempo en minutos.
# INGRESE SU CÓDIGO AQUÍ
df_times = pd.merge(df_bogota,df_2017_hospital_times,how = "inner",on= "MOVEMENT_ID")
df_times["time_minutes"] = df_times["time"]/60
# PRUEBE SU CODIGO AQUÍ
df_times.head()
# NO MODIFIQUE ESTA CELDA
df_times.head()
Genere un mapa cloroplético sobre los tiempos de viaje HACÍA los diferentes hospitales de Teusaquillo. Use la columna que recién creo como columna de clasificación.
# INGRESE Y PRUEBE SU CÓDIGO AQUÍ
f, ax = plt.subplots(1, figsize=(15, 10))
ax.set_title(u'Tiempo promedio de viaje hacia los hospitales de Teusaquillo')
##### INGRESE AQUÍ SU CÓDIGO
# Imprima un mapa cloroplético usando la columna time_minutes_2016 en
# el GeoDataFrame df_times. El mapa debe tener un título, un colormap definido,
# debe estar definido en 10 categorías.
df_times.plot(column='time_minutes', scheme="Fisher_Jenks",k=10, legend=True, cmap="Blues",figsize=(15,10),ax=ax) # Creamos un mapa coroplético sobre time_minutes_2016
##### NO MODIFIQUE EL CÓDIGO ABAJO
df_bogota.plot(color='None', edgecolor='black', linewidth=0.1, ax=ax)
ax.set_axis_off()
plt.axis('equal')
for i,row in hospital_df.iterrows():
x=row['geometry'].x
y=row['geometry'].y
plt.annotate(row['Name'], xy=(x, y), size=7, xytext=(0, 5), textcoords='offset points')
plt.plot(x, y,'o', color='#f16824')
ax.set(aspect=1)
# NO MODIFIQUE ESTA CELDA
Descargue la información de otra zona de Bogotá y evalue la accesibilidad a otro tipo de zonas como Restaurantes en Modelia.
La estratificación permite clasificar a la población en diferentes grupos socioeconómicos. La estratificación define elementos como los subsidios que asigna el gobierno para el cobro de los servicios públicos domiciliarios. Aquellas personas que viven en domicilios de estrato alto pagan más por los servicios públicos, contribuyendo a los hogaros de estrato bajo. Sin embargo, la estratificación también está determinada por las condiciones de acceso y movilidad del domicilio. Durante este ejercicio, vamos a analizar la información de estratos y luego evaluaremos las condiciones de movilidad usando la información provista por UBER Movement.
Vamos a usar los siguientes archivos:
bogota-estratos-2013.json: Contiene los polígonos correspondientes a las manzanas de Bogotá D.C.. Contiene información adicional como los estratos y la fecha en que se realizó la estratificación.bogota-localidades.geojson: Contiene la información geográfica de las 20 localidades del Distrito Capital.bogota_cadastral.json: contiene la información georeferenciada de 1160 zonas que componen a la ciudad de Bogotá D.C. (Incluído Sumapaz)bogota-cadastral-2017-2-OnlyWeekdays-HourlyAggregate.csv: contiene la información de viajes entre semana entre un origen y un destino en la ciudad de Bogotá, para el segundo trimestre de 2017. Se encuentra discriminada por la hora en la que se realizó el viaje.Este ejercicio supone un reto adicional, puesto que el JSON que contiene la información, no viene en un formato de carga directa a GeoPandas. Primero cargue el JSON y visualice la información que contiene.
df = pd.read_json('/home/dianar/ML/TallerI/bogota_estratos_2013.json')
df.head()
df['fields'][1]
Para definir un GeoDataFrame, tenga en cuenta que cada fila debe tener mínimo una columna que se llame geometry y un sistema de coordenadas de referencia.
Complete la siguiente función para construir el GeoDataFrame. Use lo aprendido en el taller de GeoPandas
from geopandas import GeoDataFrame
from shapely.geometry import Polygon, MultiPolygon
my_keys=["objectid","codigo_manzana","shape_area","shape_len","estrato"]
geometry = []
values = []
# MODIFIQUE LAS LÍNEAS QUE SE SOLICITAN
for idx, row_value in df.iterrows():
if row_value.fields.get('geo_shape', 0) == 0:
continue
if row_value.fields['geo_shape']['type'] == 'Polygon':
geometry.append( Polygon([[x,y] for x,y in row_value.fields["geo_shape"]["coordinates"][0]])
)
else:
multipo = []
for poly in row_value.fields['geo_shape']['coordinates'][0]:
multipo.append(Polygon([[x, y] for x, y in poly]))
geometry.append(MultiPolygon(multipo))
values.append([df["fields"][idx][x] for x in my_keys]
)
# PRUEBE SE CÓDIGO AQUÍ
df = pd.DataFrame(values, columns=['id', 'manzana', 'shape_area', 'shape_len', 'estrato'])
crs = {'init': 'epsg:4326'}
estratos = GeoDataFrame(df, crs=crs, geometry=geometry)
estratos.head()
# NO MODIFIQUE ESTA CELDA
df = pd.DataFrame(values, columns=['id', 'manzana', 'shape_area', 'shape_len', 'estrato'])
crs = {'init': 'epsg:4326'}
estratos = GeoDataFrame(df, crs=crs, geometry=geometry)
estratos.head()
Como puede evidenciar, hemos asociado la información de los estratos con las manzanas respectivas en el archivo json.
f, ax = plt.subplots(1, figsize=(15, 10))
ax.set_title(u'Estratos en Bogotá')
estratos.plot(column='estrato', categorical=True, legend=True, ax=ax)
ax.set_axis_off()
plt.axis('equal')
df_localidades = gpd.read_file('/home/dianar/ML/TallerI/bogota_localidades.geojson')
df_localidades = df_localidades.loc[df_localidades['CODIGO_LOC'] != '20'] # Filtrar Sumapaz
df_localidades.head()
df_localidades.loc[7,'geometry']
localidades = df_localidades[['CODIGO_LOC', 'geometry', 'SHAPE_AREA']]
localidades.rename(index=str, columns={"SHAPE_AREA": "shape_area_loc"}, inplace=True)
Haga un spatial join, entre los estratos y las localidades.
# INGRESE SU CÓDIGO AQUÍ
estratos_localidades = gpd.sjoin(estratos,localidades, how="inner", op='within')
# PRUEBE SU CÓDIGO AQUÍ
#Index_right copia el valor del indice del dataframe de la derecha, este valor no lo necesitamos
estratos_localidades.drop('index_right', axis=1, inplace=True)
estratos_localidades.head()
# NO MODIFIQUE ESTA CELDA
estratos_localidades.drop('index_right', axis=1, inplace=True)
estratos_localidades.head()
Visualice solo las manzanas que pertenecen a Kennedy en el mapa de Bogotá.
# INGRESE SU CÓDIGO AQUÍ
#df_kennedy = estratos_localidades.loc[estratos_localidades['CODIGO_LOC'] == '8']
#df_kennedy.head()
#df_kennedy.plot(column='manzana', categorical=True, legend=True, ax=ax)
#df_2017_2 = pd.read_csv('data/uber/2017-2/bogota-cadastral-2017-2-OnlyWeekdays-HourlyAggregate.csv')
#df_bogota = gpd.read_file('/home/dianar/ML/TallerI/bogota_cadastral.json')
#df_bogota.MOVEMENT_ID = df_bogota.MOVEMENT_ID.astype(np.int64)
#df_bogota = df_bogota.loc[df_bogota['MOVEMENT_ID'].isin(df_2017_2['sourceid'].unique())]
#df_bogota_filtered = df_bogota['geometry']
df_kennedy = estratos_localidades.loc[estratos_localidades['CODIGO_LOC'] == '8']
df_kennedy.plot(column='manzana', categorical=True, legend=True, ax=ax)
df_2017_2 = pd.read_csv('/home/dianar/ML/TallerI/bogota-cadastral-2017-2-OnlyWeekdays-HourlyAggregate.csv')
df_bogota = gpd.read_file('/home/dianar/ML/TallerI/bogota_cadastral.json')
df_bogota.MOVEMENT_ID = df_bogota.MOVEMENT_ID.astype(np.int64)
df_bogota = df_bogota.loc[df_bogota['MOVEMENT_ID'].isin(df_2017_2['sourceid'].unique())]
df_bogota_filtered = df_bogota['geometry']
# PRUEBE AQUÍ SU CÓDIGO
f, ax = plt.subplots(1, figsize=(15, 10))
ax.set_title(u'Localidad de Kennedy')
df_kennedy.plot(legend=True, ax=ax) # k nos permite controlar el número de clases para el mapa
df_bogota_filtered.plot(color='None', edgecolor='black', linewidth=0.1, ax=ax)
ax.set_axis_off()
plt.axis('equal')
# NO MODIFIQUE ESTA CELDA
f, ax = plt.subplots(1, figsize=(15, 10))
ax.set_title(u'Localidad de Kennedy')
df_kennedy.plot(legend=True, ax=ax) # k nos permite controlar el número de clases para el mapa
df_bogota_filtered.plot(color='None', edgecolor='black', linewidth=0.1, ax=ax)
ax.set_axis_off()
plt.axis('equal')
A continuación encontramos que cada polígono tiene asociado un área. Vamos a hallar qué estrato tiene más porcentaje de área en Kennedy.
estratos_localidades.head()
Vamos a generar el total de área por localidad y estrato. Para esto se debe generar un groupby de pandas usando las columnas CODIGO_LOC y estrato. Recuerde llamar .reset_index() para que la localidad y el estrato sigan siendo columnas del nuevo dataframe.
# INGRESE SU CÓDIGO AQUÍ
areas_groupings=estratos_localidades.groupby(['CODIGO_LOC','estrato'])[['shape_area']].sum().reset_index()
# PRUEBE AQUÍ SU CÓDIGO
areas_groupings.head()
# NO MODIFIQUE ESTA CELDA
areas_groupings.head()
Añada una columna adicional que contenga el área total de cada localidad.
# INGRESE SU CÓDIGO AQUÍ
#area_estratos_localidad = pd.merge(areas_groupings,df_localidades, how="inner", on="CODIGO_LOC")
area_total=df_localidades.groupby('CODIGO_LOC').sum().reset_index()
area_estratos_localidad=pd.merge(areas_groupings,area_total, how='inner', on='CODIGO_LOC')
area_estratos_localidad.rename(columns={'SHAPE_AREA': 'shape_area_loc'}, inplace=True)
area_estratos_localidad.drop(['OBJECTID','SHAPE_LEN'], axis=1, inplace=True)
# PRUEBE AQUÍ SU CÓDIGO
area_estratos_localidad.head()
# NO MODIFIQUE ESTA CELDA
area_estratos_localidad.head()
area_estratos_localidad['ratio'] = area_estratos_localidad['shape_area']/area_estratos_localidad['shape_area_loc']
Usando plotly, visualice el porcentaje de área por estrato en la localidad de Kennedy
from plotly import __version__
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
import cufflinks as cf
init_notebook_mode(connected=True)
cf.go_offline()
strata = area_estratos_localidad.loc[area_estratos_localidad['CODIGO_LOC'] == '8', 'estrato']
ratio = area_estratos_localidad.loc[area_estratos_localidad['CODIGO_LOC'] == '8', 'ratio']
pldfes = pd.DataFrame({'Estrato':strata,'Porcentaje':ratio})
pldfes.iplot(kind = 'bar', x = 'Estrato', y = 'Porcentaje', title = 'Distribución de área en Kennedy',xTitle = 'Estrato',
yTitle = 'Área',dimensions= (700,450), theme = 'white',color='rgb(255,205,210)')
NO MODIFIQUE ESTA CELDA

Hasta el momento, hemos hecho un análisis descriptivo de las localidades y los estratos en la ciudad de Bogotá D.C.. Sin embargo, aún no hemos relacionado esta información con los datos de tiempos de viajes entre zonas de Bogotá. Al analizar esta situación, nos encontramos con un problema:
Para anotar las zonas de uber, usaremos una función de agregación espacial, conocida como geopandas.dissolve. Sin embargo, antes debemos combinar el dataframe estratos_localidades con la información de Uber (df_bogota). A continuación, podemos observar que esto lo logramos usando gpd.sjoin. Observamos que al final del nuevo DataFrame, existe una columna MOVEMENT_ID que vincula la manzana con su respectiva zona de Uber.
df_bogota_uber = df_bogota[['MOVEMENT_ID', 'geometry']] #Versión reducida de df_bogota
# Hacemos unión espacial preguntando si una manzana se encuentra contenida en una determinada zona de Uber
df_bogota_uber = gpd.sjoin(estratos_localidades, df_bogota_uber, how='inner', op='within')
# Eliminamos index_right
df_bogota_uber.drop('index_right', axis=1, inplace=True)
df_bogota_uber.head()
Genere un nuevo DataFrame df_bogota_uber_agg, que a través de la función geopandas.dissolve agregue las manzanas que corresponden a una zona de Uber. La función de agregación que usaremos consiste en el estrato con mayor frecuencia dentro de esa zona de uber.
La función de agregación que regresa el elemento de mayor frecuencia en un grupo es:
lambda x:x.value_counts().index[0]
#df_bogota_uber.apply(lambda x: x.value_counts().index[0])
df_bogota_uber.head(15)
# INGRESE SU CÓDIGO AQUÍ
df_bogota_uber_agg = df_bogota_uber.dissolve(by='MOVEMENT_ID',aggfunc=lambda x: x.value_counts().index[0])
# PRUEBE SU CÓDIGO AQUÍ
df_bogota_uber_agg.reset_index(inplace=True)
df_bogota_uber_agg.head()
# NO MODIFIQUE ESTA CELDA
df_bogota_uber_agg.reset_index(inplace=True)
df_bogota_uber_agg.head()
df_bogota_uber_agg.count()
El resultado final son 948 zonas de Uber anotadas con estrato y localidad
Ahora queremos combinar esta información con los datos de viaje. Crearemos cuatro columnas adicionales en df_2017_2. Vamos a añadir el estrato de la zona origen, el estrato de la zona destino, la localidad de la zona origen y la localidad de la zona destino.
# Fusionamos df_2017_2 con df_bogota_uber_agg.
# Añadimos la columna source_estrato y source_loc
df_all = pd.merge(df_2017_2[['sourceid', 'dstid', 'hod', 'mean_travel_time']],
df_bogota_uber_agg[['MOVEMENT_ID', 'estrato', 'CODIGO_LOC']],
left_on = 'sourceid',
right_on = 'MOVEMENT_ID')
df_all = df_all.drop(columns=['MOVEMENT_ID'])
df_all.rename(columns={'estrato': 'source_estrato', 'CODIGO_LOC': 'source_loc'}, inplace=True)
# Fusionamos df_2017_2 con df_bogota_uber_agg.
# Añadimos la columna dst_estrato y dst_loc
df_all = pd.merge(df_all,
df_bogota_uber_agg[['MOVEMENT_ID', 'estrato', 'CODIGO_LOC']],
left_on = 'dstid',
right_on = 'MOVEMENT_ID')
df_all = df_all.drop(columns=['MOVEMENT_ID'])
df_all.rename(columns={'estrato': 'dst_estrato', 'CODIGO_LOC': 'dst_loc'}, inplace=True)
df_all.head()
Teniendo los estratos origen y destino, podemos formularnos preguntas como:
# ESCRIBA SU CÓDIGO AQUÍ
hpico = [7,8,9,17,18,19]
# Filtre df_all por las horas pico (7, 8, 9, 17, 18, 19)
df_all = df_all[df_all["hod"].isin(hpico)]
# Filtre el anterior dataframe también por source_estrato igual a 6
df_all = df_all[df_all["source_estrato"]==6]
# Genere df_from_e6_mean agrupando los valores por el estrato destino y use mean() como función de agregación
df_from_e6_mean = df_all.groupby("dst_estrato").mean().reset_index()
# Genere df_from_e6_count agrupando los valores por el estrato destino y use count() como función de agregación
df_from_e6_count = df_all.groupby("dst_estrato").count().reset_index()
# PRUEBE SU CÓDIGO AQUÍ
print(df_from_e6_mean['mean_travel_time'])
print('---------------')
print(df_from_e6_count['sourceid'])
# NO MODIFIQUE LA SIGUIENTE CELDA
print(df_from_e6_mean['mean_travel_time'])
print('---------------')
print(df_from_e6_count['sourceid'])
Genere el diagrama de líneas para la frecuencia de viajes y para el tiempo promedio entre estratos en Plot.ly
# INGRESE SU CÓDIGO AQUÍ
df_from_e6_count.iplot(x="dst_estrato",y = "mean_travel_time", xTitle="Estratos",yTitle="# de Viajes", title =" Frecuencia de viajes desde estrato 6",
dimensions= (700,450))
NO MODIFIQUE ESTA CELDA

# INGRESE SU CÓDIGO AQUÍ
df_from_e6_mean.iplot(x="dst_estrato",y="mean_travel_time",title="Frecuencia de viajes desde estrato 6",yTitle="# de Viajes",
xTitle="Estratos",dimensions= (700,450))
NO MODIFIQUE ESTA CELDA:

Cree una gráfica de Plot.ly que permita visualizar la distribución de estratos de todas las localidades. (Parecido al ejercicio de porcentaje de triples por posición y por equipos en el taller de la NBA)